﻿using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace ThresholdAssistant.Tools
{
    /// <summary>
    /// 控制任务数量的调度器
    /// </summary>
    public class LimitedTaskScheduler : TaskScheduler
    {
        //var scheduler = new LimitedTaskScheduler(数量);
        //var factory = new TaskFactory(scheduler);
        //Task task = Factory.StartNew(() => CreateFile(taskInfo), taskInfo.CancelToken.Token);

        /// <summary>
        /// 当前正在排队或运行的并行数量
        /// </summary>
        private int _delegatesQueuedOrRunning = 0;

        /// <summary>
        /// 最大的并行数量
        /// </summary>
        private readonly int _maxDegreeOfParallelism;

        /// <summary>
        /// 指示当前线程是否正在处理工作项
        /// </summary>
        [ThreadStatic]
        private static bool _currentThreadIsProcessingItems;

        /// <summary>
        /// 要执行的任务列表
        ///     受锁定保护（_tasks）
        /// </summary>
        private readonly LinkedList<Task> _tasks = new LinkedList<Task>();

        /// <summary>
        /// 获取此计划程序支持的最大的并行数量
        /// </summary>
        public sealed override int MaximumConcurrencyLevel { get { return _maxDegreeOfParallelism; } }

        /// <summary>
        /// 创建一个具有指定并行度的新实例
        /// </summary>
        /// <param name="maxDegreeOfParallelism">最大的并行数量</param>
        public LimitedTaskScheduler(int maxDegreeOfParallelism)
        {
            if (maxDegreeOfParallelism < 1)
            {
                throw new ArgumentOutOfRangeException("maxDegreeOfParallelism");
            }
            _maxDegreeOfParallelism = maxDegreeOfParallelism;
        }

        /// <summary>
        /// 将任务排入调度程序队列
        /// </summary>
        /// <param name="task">任务对象</param>
        protected sealed override void QueueTask(Task task)
        {
            lock (_tasks)
            {
                //将任务添加到要处理的任务列表中。如果没有足够的
                //当前排队或正在运行以处理任务的委派，安排另一个
                _tasks.AddLast(task);
                if (_delegatesQueuedOrRunning < _maxDegreeOfParallelism)
                {
                    ++_delegatesQueuedOrRunning;
                    NotifyThreadPoolOfPendingWork();
                }
            }
        }

        /// <summary>
        /// 通知线程池有工作要为这个调度程序执行 
        /// </summary>
        private void NotifyThreadPoolOfPendingWork()
        {
            ThreadPool.UnsafeQueueUserWorkItem(callBack =>
            {
                //请注意，当前线程现在正在处理工作项。
                //这对于将任务内联到此线程中是必要的。
                _currentThreadIsProcessingItems = true;
                try
                {
                    //处理队列中的所有可用项目
                    while (true)
                    {
                        Task item;
                        lock (_tasks)
                        {
                            //当没有更多的项目要处理时，
                            //请注意，我们已经完成了处理，然后离开。
                            if (_tasks.Count == 0)
                            {
                                --_delegatesQueuedOrRunning;
                                break;
                            }

                            //从队列中获取下一个项目
                            item = _tasks.First.Value;
                            _tasks.RemoveFirst();
                        }

                        //执行我们从队列中提取的任务
                        TryExecuteTask(item);
                    }
                }
                finally
                {
                    //我们已经处理完当前线程上的项目
                    _currentThreadIsProcessingItems = false;
                }
            }, null);
        }

        /// <summary>
        /// 尝试在当前线程上执行指定的任务
        /// </summary>
        /// <param name="task">当前任务</param>
        /// <param name="taskWasPreviouslyQueued"></param>
        /// <returns></returns>
        protected sealed override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
        {
            if (!_currentThreadIsProcessingItems)
            {
                //如果这个线程还没有处理任务，我们就不支持内联如果这个线程还没有处理任务，我们就不支持内联
                return false;
            }

            //如果任务以前已排队，请将其从队列中删除
            if (taskWasPreviouslyQueued)
            {
                //尝试运行任务
                if (TryDequeue(task))
                {
                    return TryExecuteTask(task);
                }
                else
                {
                    return false;
                }
            }
            else
            {
                return TryExecuteTask(task);
            }
        }

        /// <summary>
        /// 尝试从计划程序中删除以前计划的任务
        /// </summary>
        /// <param name="task">当前任务</param>
        /// <returns></returns>
        protected sealed override bool TryDequeue(Task task)
        {
            lock (_tasks)
            {
                return _tasks.Remove(task);
            }
        }

        /// <summary>
        /// 获取当前在此计划程序上计划的任务的可枚举值
        /// </summary>
        /// <returns></returns>
        protected sealed override IEnumerable<Task> GetScheduledTasks()
        {
            bool lockTaken = false;
            try
            {
                Monitor.TryEnter(_tasks, ref lockTaken);
                if (lockTaken)
                {
                    return _tasks;
                }
                else
                {
                    throw new NotSupportedException();
                }
            }
            finally
            {
                if (lockTaken)
                {
                    Monitor.Exit(_tasks);
                }
            }
        }
    }
}